#include "SimpleLogger.h"
#include <stdio.h>
 
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <pthread.h>
#include <sys/time.h>
#include <string.h>

static std::string getTimestamp() {
    time_t tt = time(NULL);
    struct tm *stm = localtime(&tt);

    struct timeval tv;
    gettimeofday(&tv, NULL);
    int ms = tv.tv_usec / 1000;

    char timestamp[24] = {0};
    sprintf(timestamp, "%02d-%02d %02d:%02d:%02d.%03d", 1 + stm->tm_mon, stm->tm_mday, stm->tm_hour,
            stm->tm_min, stm->tm_sec, ms);

    return timestamp;
}

 std::string SimpleLoggerAppendData::toString(const LoggerAppendData &data)
 {
    char buf[256] = {'\0'};
    pthread_t tid = pthread_self();
    #define get_filename(x) strrchr(x, '/') ? strrchr(x, '/') + 1 : x
    sprintf(buf,"Thr[%u] %s:%d ::%s \t",  (uint32_t) tid, get_filename(data.fileName.c_str()), data.line, data.funcName.c_str());
    #undef get_filename
    return buf;
 }



void LoggerConsoleSink::printLine(int level, const char *buf) {

    auto fmt = "%s\tunknow: %s\n";
    switch(level) {
        case LOG_FATAL:      fmt = "%s\t[fatal] %s\n"; break;
        case LOG_ERROR:      fmt = "%s\t[error] %s\n"; break;
        case LOG_WARN:       fmt = "%s\t[warn ] %s\n"; break;
        case LOG_INFO:       fmt = "%s\t[info ] %s\n"; break;
        case LOG_DEBUG:      fmt = "%s\t[debug] %s\n"; break;
        case LOG_VERBOSE:    fmt = "%s\t[verbo] %s\n"; break;
        default: break;
    }
 

    std::string timestamp = getTimestamp();

    printf(fmt, timestamp.c_str(), buf);
    return;
}

void SimpleLogger::log(LoggerAppendData *data,LogLevelType nLogLevel, const std::string &content)
{
    if (nLogLevel > (LogLevelType)logLevel)
    {
        return;
    }
    std::string prefix = append->toString(*data);
    char buf[1024] = {'\0'};
    sprintf(buf,"%s %s",prefix.c_str(),content.c_str());
    for (auto loggerSink : sinks) {
        loggerSink->printLine(nLogLevel, buf);
    }
}
/**
 * @brief 
 * <bean name="log" interface="ILogger" implemation="SimpleLogger">
 *  <log level="6"/>
 *  <sink implemation="LoggerConsoleSink"/>
 *  <append implemation="SimpleLoggerAppendData" filter="all"/>
 * </bean>
 */

void SimpleLogger::onLoad(INode *node)
{
    INode *child = node->childNode(NULL);
    while (child) {
        if (child->name == "log") {
            logLevel = child->get<int>("level");
        }
        std::string implemation = child->getStr("implemation");
        if (child->name == "sink") {
            IPlugin *bean = (IPlugin*)IPlugin::loadPlugin(implemation.c_str());
            bean->onLoad(node);
            sinks.push_back((ILoggerSink*) bean);
        }
        if (child->name == "append") {
            IPlugin *bean = (IPlugin*)IPlugin::loadPlugin(implemation.c_str());
            bean->onLoad(node);
            append = (ILoggerAppendData*) bean;
        }
        child = child->nextNode(NULL);
    }
}

REG_PLUGIN(SimpleLogger)
REG_PLUGIN(LoggerConsoleSink)
REG_PLUGIN(SimpleLoggerAppendData)